1 /** 2 Copyright: Copyright (c) 2017, Joakim Brännström. All rights reserved. 3 License: MPL-2 4 Author: Joakim Brännström (joakim.brannstrom@gmx.com) 5 6 This Source Code Form is subject to the terms of the Mozilla Public License, 7 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain 8 one at http://mozilla.org/MPL/2.0/. 9 10 Handles console logging in pretty colors. 11 12 The module disables colors when stdout and stderr isn't a TTY that support 13 colors. This is to avoid ASCII escape sequences in piped output. 14 15 Credit goes to the developers of Dub. A significant part of the color handling 16 is copied from that project. 17 */ 18 module code_checker.logger; 19 20 import std.algorithm : among; 21 import std.stdio : writeln, writefln, stderr, stdout; 22 import logger = std.experimental.logger; 23 import std.experimental.logger : LogLevel; 24 25 import colorize : Color, Background, Mode; 26 27 /// The verbosity level of the logging to use. 28 enum VerboseMode { 29 /// Warning+ 30 minimal, 31 /// Info+ 32 info, 33 /// Trace+ 34 trace, 35 /// Warnings+ 36 warning, 37 } 38 39 void confLogger(VerboseMode mode) { 40 switch (mode) { 41 case VerboseMode.info: 42 logger.globalLogLevel = logger.LogLevel.info; 43 logger.sharedLog = new SimpleLogger(logger.LogLevel.info); 44 break; 45 case VerboseMode.trace: 46 logger.globalLogLevel = logger.LogLevel.all; 47 logger.sharedLog = new DebugLogger(logger.LogLevel.all); 48 logger.info("Debug mode activated"); 49 break; 50 case VerboseMode.warning: 51 logger.globalLogLevel = logger.LogLevel.warning; 52 logger.sharedLog = new SimpleLogger(logger.LogLevel.info); 53 break; 54 default: 55 logger.globalLogLevel = logger.LogLevel.info; 56 logger.sharedLog = new SimpleLogger(logger.LogLevel.info); 57 } 58 } 59 60 private: 61 62 /** 63 * Whether to print text with colors or not, defaults to true but will be set 64 * to false in initLogging() if stdout or stderr are not a TTY (which means the 65 * output is probably being piped and we don't want ASCII escape chars in it) 66 */ 67 shared bool _printColors = true; 68 shared bool _isColorsInitialized = false; 69 70 // The width of the prefix. 71 immutable _prefixWidth = 8; 72 73 /** 74 * It will detect whether or not stdout/stderr are a console/TTY and will 75 * consequently disable colored output if needed. 76 * 77 * Forgetting to call the function will result in ASCII escape sequences in the 78 * piped output, probably an undesiderable thing. 79 */ 80 void initLogging() @trusted { 81 if (_isColorsInitialized) 82 return; 83 scope (exit) 84 _isColorsInitialized = true; 85 86 // Initially enable colors, we'll disable them during this functions if we 87 // find any reason to 88 _printColors = true; 89 90 version (Windows) { 91 _printColors = false; 92 } else { 93 import core.stdc.stdio; 94 import core.sys.posix.unistd; 95 96 if (!isatty(STDERR_FILENO) || !isatty(STDOUT_FILENO)) 97 _printColors = false; 98 } 99 } 100 101 class SimpleLogger : logger.Logger { 102 this(const LogLevel lvl = LogLevel.warning) @safe { 103 super(lvl); 104 initLogging; 105 } 106 107 override void writeLogMsg(ref LogEntry payload) @trusted { 108 auto out_ = stderr; 109 auto use_color = Color.red; 110 auto use_mode = Mode.bold; 111 const use_bg = Background.black; 112 113 switch (payload.logLevel) { 114 case LogLevel.trace: 115 out_ = stdout; 116 use_color = Color.white; 117 use_mode = Mode.init; 118 break; 119 case LogLevel.info: 120 out_ = stdout; 121 use_color = Color.white; 122 break; 123 default: 124 } 125 126 import std.conv : to; 127 import colorize; 128 129 out_.writefln("%s: %s", payload.logLevel.to!string.color(use_color, 130 use_bg, use_mode), payload.msg); 131 } 132 } 133 134 class DebugLogger : logger.Logger { 135 this(const logger.LogLevel lvl = LogLevel.trace) { 136 super(lvl); 137 initLogging; 138 } 139 140 override void writeLogMsg(ref LogEntry payload) @trusted { 141 auto out_ = stderr; 142 auto use_color = Color.red; 143 auto use_mode = Mode.bold; 144 const use_bg = Background.black; 145 146 switch (payload.logLevel) { 147 case LogLevel.trace: 148 out_ = stdout; 149 use_color = Color.white; 150 use_mode = Mode.init; 151 break; 152 case LogLevel.info: 153 out_ = stdout; 154 use_color = Color.white; 155 break; 156 default: 157 } 158 159 import std.conv : to; 160 import colorize; 161 162 out_.writefln("%s: %s [%s:%d]", payload.logLevel.to!string.color(use_color, 163 use_bg, use_mode), payload.msg, payload.funcName, payload.line); 164 } 165 }